
#ifndef __SDK__STORAGE_H__INCLUDE__
#define __SDK__STORAGE_H__INCLUDE__

#include <map>
#include <string>

#include "ServiceBase.h"
#include "Stream.h"
#include "Task.h"
#include "TaskProcessor.h"
#include "Utility/Mutex.h"
#include "Utility/TaskScheduler.h"


namespace nebula { namespace sdk {

struct BTreeNodeCacheWrapper;

class Storage : public ServiceBase
{
	typedef std::map<std::string, IStreamCollection*> StreamTable;
	enum SegmentOpenMode
	{
		som_r  = 1,
		som_w  = 2,
		som_rw = som_r | som_w,
	};

public:
	Storage();
	virtual ~Storage();

public:
	//////////////////////////////////////////////////////////////////////////
	//TaskBase required event handlers
	virtual NEBULA_ERROR_CODE OnInitialize () = 0;
	virtual NEBULA_ERROR_CODE OnStart      () = 0;
	virtual void              OnStop       () = 0;
	virtual void              OnFinalize   () = 0;

public:
	//////////////////////////////////////////////////////////////////////////
	//Storage required event handlers

	//This virtual member will be called before the save operation of the task
	// built-in behavior of this event handler is empty
	// rewrite the virtual function to describe customized process
	virtual NEBULA_ERROR_CODE OnSaveTask(ITask* task);

	//This virtual member will be called before the task is added to the sync queue
	// built-in behavior of this event handler is empty
	// rewrite the virtual function to describe customized process
	virtual NEBULA_ERROR_CODE OnSyncTask(ITask* task);

	//This virtual member will be called before the task is added to the acknowledge queue
	// built-in behavior of this event handler is empty
	// rewrite the virtual function to describe customized process
	virtual NEBULA_ERROR_CODE OnAckTask (ITask* task);

	//This virtual member will be called per storage::_purge_interval seconds, which can
	// be set by client code via calling SetPurgeInterval() function.
	// default behavior of this event handler is empty
	virtual NEBULA_ERROR_CODE OnPurgeData();

public:
	//////////////////////////////////////////////////////////////////////////
	//Storage required behaviors setting interface

	//override this function to describe what segment the task belongs to
	//by default, "default" is the default segment
	//note: the folder hierarchy will be in the form of /storage/<data name>/<segement>/...
	virtual NEBULA_ERROR_CODE GetTaskSegment(ITask* task, char* segment, int size);

	//override this function to describe the key of the task
	//by default, task id plays such role
	virtual NEBULA_ERROR_CODE GetTaskKey(ITask* task, char* segment, int size);

	//override this function to limit the sync count of a task among storage inherited services
	//by default, a fixed number of 3 is used
	virtual int GetTaskSyncLimit(ITask* task);

	//override this function to describe the task format
	//by default, Plain Text Task is used
	virtual ITask* AllocateTask();

	//override this function to describe the manner on which the task should be filtered
	//the service self-cycle will be prevented by default implementation
	virtual bool FilterTask (const char* target_node_name, const ITask* task);

	//override this function to describe how the task is queried
	//by default, task is indexed by root task id
	virtual NEBULA_ERROR_CODE QueryTask (IRequest* request, ITask** task);

	//override this function to describe how the task is deleted
	//by default, task is indexed by root task id
	virtual NEBULA_ERROR_CODE DeleteTask(IRequest* request);

protected:
	//////////////////////////////////////////////////////////////////////////
	//Storage provided operations' interfaces

	//call this function to set the data name, which will be used as
	//the url of data input as well as the sub dictionary inside data/storage/
	//note: this function must be called before InitializeStorage()
	NEBULA_ERROR_CODE SetDataName (const char* name);

	//call this function to set the data alias -- the alias of the segment
	// and the alias of key. these 2 alias will be used for both the query 
	// and delete url to identify a specific task or a segment
	//note: this function must be called before InitializeStorage()
	NEBULA_ERROR_CODE SetDataAlias(const char* segment, const char* key);

	//call this function to set the interval of data purge operation
	// the unit of the interval is seconds 
	const long        SetPurgeInterval(const long seconds);

	//Inherit classes should call this function in OnInitialize() event handler
	// to ensure the storage is initialized properly
	NEBULA_ERROR_CODE InitializeStorage();

	//Inherit classes should call this function in OnFinalize() event handler
	// to ensure the storage is finalized properly
	void              FinalizeStorage();

	//Inherit classes should call this function in OnStart() event handler
	// to ensure the storage is started properly
	NEBULA_ERROR_CODE StartStorage();

	//Inherit classes should call this function in OnStop() event handler
	// to ensure the storage is stopped properly
	void              StopStorage ();

	//Inherit classes could call this function to perform an operation of saving task to disc
	NEBULA_ERROR_CODE SaveTask  (const char* segment, const char* key, ITask* task);

	//Inherit classes could call this function to find a task from disc
	NEBULA_ERROR_CODE ReadTask  (const char* segment, const char* key, ITask** task);

	//Inherit classes could call this function to remove task from disc
	NEBULA_ERROR_CODE RemoveTask(const char* segment, const char* key);

	//Inherit classes could call this function to remove a segment from disc and cache
	NEBULA_ERROR_CODE RemoveSegment(const char* name);

private:
	NEBULA_ERROR_CODE SetDefaultSyncCount(int value);
	int               GetDefaultSyncCount();

	IStreamCollection* OpenSegment(const char* name, SegmentOpenMode mode);

	NEBULA_ERROR_CODE Load();
	NEBULA_ERROR_CODE OpenStreamCollection(const char* path);

private:
	//static agents
	static ITask*            TaskAllocator(IService*);
	static bool              TaskFilter   (IService*, const char*, const ITask*);
	static NEBULA_ERROR_CODE TaskQuerier  (IService*, IRequest*, ITask**);
	static NEBULA_ERROR_CODE TaskDeleter  (IService*, IRequest*);

private:
	NEBULA_ERROR_CODE InputTaskHandler(std::auto_ptr<ITask> task);
	NEBULA_ERROR_CODE PurgeTaskHandler(bool &repeat_immediately);

	bool IsSyncLimitReached(ITask* task);

private:
	std::string _root_path;
	StreamTable _stream_table;

	ITodoTaskQueue* _input_queue;
	IDoneTaskQueue* _sync_queue;
	IDoneTaskQueue* _meta_queue;

	TaskProcessor<Storage>          _input_processor;
	utility::TaskScheduler<Storage> _purge_scheduler;

	int _default_sync_count;

	long        _purge_interval;
	std::string _data_name;
	std::string _segment_alias;
	std::string _key_alias;
	BTreeNodeCacheWrapper* _node_cache;
	
};

}} //end of nebula::sdk

#endif // __SDK__STORAGE_H__INCLUDE__

